GitLab 进阶:配置 Vue 项目 CI/CD 任务
Vue 项目 CI/CD 流程概览
代码提交
|
v
┌─────────┐ ┌─────────┐ ┌─────────────┐
│ Build │ -> │ Test │ -> │ Deploy │
│ 构建阶段 │ │ 测试阶段 │ │ 部署阶段 │
└─────────┘ └─────────┘ └─────────────┘
| |
v v
npm install rsync/SCP 传输
npm run build dist/ -> 远程服务器
artifacts: dist/ 重启 Nginx 服务
text
完整 .gitlab-ci.yml 配置
基础版:构建 + 部署到服务器
# .gitlab-ci.yml - Vue 项目 CI/CD
stages:
- build
- deploy
# ─── 构建阶段 ───
build-job:
stage: build
image: node:18-alpine
before_script:
- npm config set registry https://registry.npmmirror.com
script:
- npm install
- npm run build
- ls -la dist/
artifacts:
paths:
- dist/
expire_in: 1 day
tags:
- docker
# ─── 部署阶段 ───
deploy-job:
stage: deploy
image: alpine:latest
before_script:
# 安装 rsync 和 SSH 客户端
- apk add --no-cache rsync openssh-client
# 创建 SSH 目录并配置密钥
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
# 禁用严格主机密钥检查(生产环境建议配置 known_hosts)
- echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
script:
# 使用 rsync 传输 dist 目录到远端服务器
- rsync -avz --delete dist/ $DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH
rules:
# 仅在 main 分支触发部署
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
tags:
- docker
yaml
进阶版:多环境区分 + 测试阶段
# .gitlab-ci.yml - Vue 项目多环境 CI/CD
stages:
- build
- test
- deploy_staging
- deploy_production
# ─── 全局缓存配置 ───
cache: &default_cache
key:
files:
- package-lock.json
paths:
- node_modules/
policy: pull
# ─── 构建阶段 ───
build-job:
stage: build
image: node:18-alpine
cache:
<<: *default_cache
policy: pull-push
before_script:
- npm config set registry https://registry.npmmirror.com
script:
- npm ci
- npm run build
- ls -la dist/
artifacts:
paths:
- dist/
expire_in: 1 day
tags:
- docker
# ─── 测试阶段 ───
test-job:
stage: test
image: node:18-alpine
cache:
<<: *default_cache
before_script:
- npm config set registry https://registry.npmmirror.com
script:
- npm ci
- npm run test:unit
coverage: '/All files[^|]*\|[^|]*\s+([\d.]+)/'
artifacts:
reports:
junit: test-results.xml
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
tags:
- docker
# ─── 部署到 Staging ───
deploy_staging:
stage: deploy_staging
image: alpine:latest
environment:
name: staging
url: https://staging.example.com
before_script:
- apk add --no-cache rsync openssh-client
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
script:
- rsync -avz --delete dist/ $STAGING_USER@$STAGING_HOST:$STAGING_PATH
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
tags:
- docker
# ─── 部署到 Production ───
deploy_production:
stage: deploy_production
image: alpine:latest
environment:
name: production
url: https://www.example.com
before_script:
- apk add --no-cache rsync openssh-client
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
script:
- rsync -avz --delete dist/ $DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual # 生产环境需要手动触发
tags:
- docker
yaml
GitLab Pages 部署方案
如果不需要独立服务器,可直接使用 GitLab Pages 托管静态站点:
# .gitlab-ci.yml - GitLab Pages 部署 Vue 项目
image: node:18-alpine
cache:
paths:
- node_modules/
stages:
- deploy
pages:
stage: deploy
before_script:
- npm config set registry https://registry.npmmirror.com
script:
- npm ci
- npm run build
- mv public public-vue # 备份原有 public 目录
- mv dist public # GitLab Pages 要求输出目录名为 public
artifacts:
paths:
- public # artifacts 路径必须是 public
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
yaml
GitLab Pages 要点:Job 名称必须为
pages,输出目录必须为public,artifacts 路径必须指向public。部署完成后可通过https://<username>.gitlab.io/<project>访问。
配置详解
构建阶段(Build)
镜像选择
build-job:
image: node:18-alpine
yaml
node:18-alpine:基于 Alpine Linux 的轻量 Node.js 镜像,体积约 50MB- 镜像名称中不能有空格,否则会触发语法错误(编辑器会显示红色波浪线)
构建脚本
before_script:
- npm config set registry https://registry.npmmirror.com
script:
- npm install
- npm run build
- ls -la dist/
yaml
before_script:设置淘宝镜像加速依赖安装npm install:安装项目依赖(推荐使用npm ci以确保依赖一致性)npm run build:执行 Vue 构建(Vite 或 webpack)ls -la dist/:验证构建产物
npm install vs npm ci
| 命令 | 行为 | 适用场景 |
|---|---|---|
npm install | 根据 package.json 解析并安装,可能更新 lock 文件 | 本地开发 |
npm ci | 严格按 package-lock.json 安装,删除现有 node_modules | CI/CD 环境(推荐) |
制品(Artifacts)配置
artifacts:
paths:
- dist/
expire_in: 1 day
yaml
artifacts 关键字将构建产物保存为可下载的制品:
paths:指定要保存的文件/目录expire_in:制品过期时间
构建完成后,可在 Job 详情页点击 Browse 或 Download 查看制品。
缓存优化
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
yaml
通过缓存 node_modules,避免每次构建都重新下载全部依赖,显著缩短构建时间(通常可从 2 分钟降到 30 秒以内)。
测试阶段(Test)
test-job:
stage: test
script:
- npm run test:unit
coverage: '/All files[^|]*\|[^|]*\s+([\d.]+)/'
yaml
- 测试阶段在构建之后执行,用于验证代码质量
coverage关键字可从测试输出中提取覆盖率数据- 可配置
artifacts:reports生成测试报告和覆盖率报告
部署阶段(Deploy)
部署镜像
deploy-job:
image: alpine:latest
yaml
alpine:latest 是一个极小的 Linux 镜像(约 3MB),适合部署任务。
注意:deploy-job 必须单独指定镜像。如果未指定,会继承全局的 node 镜像,该镜像不包含 rsync 和 openssh-client,导致部署失败。
SSH 密钥配置
before_script:
- apk add --no-cache rsync openssh-client
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
yaml
密钥通过 CI/CD 变量注入,避免在代码仓库中暴露敏感信息。
rsync 部署
script:
- rsync -avz --delete dist/ $DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH
yaml
rsync 参数说明:
| 参数 | 说明 |
|---|---|
-a | 归档模式,保留权限、时间戳等 |
-v | 显示详细输出 |
-z | 压缩传输 |
--delete | 删除目标目录中源目录没有的文件 |
rsync 优势:支持增量传输(差异更新),只传输变更的文件,部署效率高。相比 SCP 全量上传,rsync 在文件较多时效率优势明显。
另一种方案:SCP 上传
script:
# SCP 不支持增量传输,全量上传
- scp -r dist/* $DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH
yaml
环境关键字(environment)
environment:
name: staging
url: https://staging.example.com
yaml
environment 关键字用于:
- 在 GitLab 界面 Deployments > Environments 中追踪部署状态
- 支持按环境作用域管理变量(如 staging 和 production 使用不同的 API 密钥)
- 支持 停止环境 操作,用于清理临时环境
配置 CI/CD 变量
在 GitLab 界面设置敏感信息:
- 进入项目 -> Settings > CI/CD > Variables
- 点击 Add variable
必需变量
| 变量名 | 说明 | 示例值 | 属性 |
|---|---|---|---|
SSH_PRIVATE_KEY | SSH 私钥内容 | -----BEGIN OPENSSH PRIVATE KEY-----... | Protected, Masked |
DEPLOY_USER | 远程服务器用户名 | deploy | - |
DEPLOY_HOST | 远程服务器地址 | 192.168.1.100 | - |
DEPLOY_PATH | 部署目标路径 | /var/www/html | - |
DEPLOY_PASS | SSH 密码(如使用密码认证) | your-password | Protected, Masked |
多环境变量配置
当 staging 和 production 使用不同服务器时,可通过 环境作用域 管理:
┌──────────────────────────────────────────────┐
│ Settings > CI/CD > Variables │
│ │
│ DEPLOY_HOST scope: staging = 10.0.1.100 │
│ DEPLOY_HOST scope: production = 10.0.2.100 │
│ │
│ DEPLOY_PATH scope: staging = /srv/staging │
│ DEPLOY_PATH scope: production = /srv/www │
└──────────────────────────────────────────────┘
text
变量添加方式:
- 添加变量时选择 Environments 下拉框
- 选择
staging或production - 同名变量在不同环境下可以有不同的值
Protected 变量注意事项
┌──────────────────────────────────────┐
│ 变量设置: Protected = true │
│ │
│ 限制: 仅在保护分支(如 main)中可用 │
│ 其他分支无法读取该变量 │
└──────────────────────────────────────┘
text
关键操作:
- 将 main 分支设为保护分支:
- 进入项目 -> Settings > Repository > Protected branches
- 选择
main分支 - 设置 Allowed to merge: Maintainers
- 设置 Allowed to push: Maintainers
- 点击 Protect
- 如果变量需要在所有分支中使用,不要勾选 Protected 选项
常见陷阱:如果流水线执行失败但变量配置正确,检查当前分支是否为保护分支。Protected 变量只能在保护分支中使用,非保护分支读取时值为空。
流水线执行流程
1. 开发者推送代码到 main 分支
|
v
2. GitLab 检测到 .gitlab-ci.yml,创建 Pipeline
|
v
3. build-job 执行
├── 下载 node:18-alpine 镜像
├── npm install(淘宝源加速)
├── npm run build(Vue 构建)
└── 上传 dist/ 为 artifacts
|
v
4. deploy-job 执行
├── 下载 alpine:latest 镜像
├── 注入 SSH 密钥
├── rsync 传输 dist/ 到远端服务器
└── 部署完成
text
多环境执行流程
代码推送到 main 分支
|
v
build-job(构建)
|
v
test-job(单元测试 + 覆盖率)
|
v
deploy_staging(自动部署到 Staging)
|
v
deploy_production(手动触发部署到 Production)
text
流水线编辑器
GitLab 提供 Web 端的 CI/CD Pipeline Editor,支持:
- 在线编辑
.gitlab-ci.yml,实时语法检查 - 关键字自动提示(如输入
artifacts会显示paths、expire_in等子属性) - 将鼠标悬停在关键字上可查看官方文档说明(Reference)
- 保存后立即触发新的流水线执行
访问方式:项目 -> Build > Pipeline editor
常见问题排查
Runner 未分配
错误:This job is stuck because the project doesn't have any runners assigned
text
解决方案:
- 进入 Settings > CI/CD > Runners
- 找到已注册的 Runner
- 勾选 Run untagged jobs(允许运行无标签任务)
- 或者在 Job 中添加对应的
tags
变量无法读取
错误:$SSH_PRIVATE_KEY 变量为空
text
原因:变量设置了 Protected,但当前分支不是保护分支。
解决方案:
- 方案一:将当前分支设为保护分支
- 方案二:取消变量的 Protected 属性
deploy-job 镜像缺失
错误:deploy job 执行失败
text
原因:deploy-job 未指定 image,使用了 node 镜像(不含 rsync)。
解决方案:确保 deploy-job 明确指定 image: alpine:latest。
rsync 权限问题
错误:Permission denied (publickey)
text
解决方案:
- 确认 SSH_PRIVATE_KEY 变量内容完整(包含 BEGIN/END 行)
- 确认远端服务器已配置对应的公钥到
~/.ssh/authorized_keys - 确认文件权限
chmod 600 ~/.ssh/id_rsa
制品跨 Job 共享
构建和部署两个 Job 共享 artifacts 目录。build-job 生成 dist/ 并上传为制品后,deploy-job 可以直接访问该目录,无需额外下载操作。
参考配置文档
GitLab 官方提供了丰富的 CI/CD 示例,涵盖常见场景:
- Vue 项目示例:包含单元测试、构建、发布完整流程
- NPM 发布:自动化 npm 包发布
- 多项目 Pipeline:跨项目触发
- 端到端测试:集成浏览器测试
- GitLab Pages:静态站点托管
建议在 GitLab CI/CD Examples 中搜索 Vue,参考官方推荐的项目配置模板。
实用参考链接
↑